/**
 * @authors Cherish
 * @date    2021/6/22 上午10:43
 * @description 基于axios的封装
 */

import axios, { AxiosRequestConfig, AxiosResponse } from 'axios';
import qs from 'qs'

/** 
 * 1. 添加一个请求拦截器 （于transformRequest之前处理）
 */
axios.interceptors.request.use((config: AxiosRequestConfig) => {
  // Do something before request is send
  if (localStorage.getItem('token')) {
    config.headers['token'] = 'Token'; // 自定义请求头部
  }
  return config;
}, (error) => {
  // 当出现请求错误是做一些处理
  return Promise.reject(error);
});

/** 
 * 2. 添加一个返回拦截器 （于transformResponse之后处理）
 * 返回的数据类型默认是json，若是其他类型（text）就会出现问题，因此用try,catch捕获异常
 */
axios.interceptors.response.use((response: AxiosResponse<any>) => {
  return handleResponse(response);
}, (error) => {
  // 对返回的错误进行一些处理
  if (error.code === 'ECONNABORTED') {// 超时处理
    return;
  }
  return Promise.reject(handleResponse(error));
});


/**
 * 3. 接口请求返回
 * @param response 
 * @returns 
 */
function handleResponse (response: AxiosResponse) {
  // 如果http状态码正常，则直接返回数据
  if (response) {
    // -1000 自己定义，连接错误的status
    const status = response.status || -1000;
    if ((status >= 200 && status < 300) || status === 304) {
      // 如果不需要除了data之外的数据，可以直接 return response.data
      return response.data;
    } else {
      let errorInfo = '';
      switch (status) {
        case -1:
          errorInfo = '远程服务响应失败,请稍后重试';
          break;
        case 400:
          errorInfo = '400: 错误请求';
          break;
        case 401:
          errorInfo = '401: 访问令牌无效或已过期';
          break;
        case 403:
          errorInfo = '403: 拒绝访问';
          break;
        case 404:
          errorInfo = '404：资源不存在';
          break;
        case 405:
          errorInfo = '405: 请求方法未允许';
          break;
        case 408:
          errorInfo = '408: 请求超时';
          break;
        case 500:
          errorInfo = '500：访问服务失败';
          break;
        case 501:
          errorInfo = '501：未实现';
          break;
        case 502:
          errorInfo = '502：无效网关';
          break;
        case 503:
          errorInfo = '503: 服务不可用';
          break;
        default:
          errorInfo = `连接错误${status}`;
      }
      return {
        status,
        msg: errorInfo
      };
    }
  }
  // 异常状态下，把错误信息返回去
  return {
    status: -404,
    msg: '网络异常'
  };
}

/**
 * 4. 接口请求axios封装
 *
 * @export
 * @param {string} url
 * @param {*} {
 *   method = 'post',
 *   timeout = 60000,
 *   data = {},
 *   headers = {},
 *   queryParams = {},
 *   dataType = 'json'
 * }
 * @return {*} 
 */
const request = ({
  url = '',
  method = 'post',
  timeout = 60000,
  data = {},
  headers = {},
  params = {},
  responseType = 'json'
}) => {

  headers = Object.assign(method === 'get' ? {
    'Accept': 'application/json',
    'Content-Type': 'application/json; charset=UTF-8'
  } : {
    'Content-Type': 'application/json; charset=UTF-8'
  }, headers);

  const defaultConfig = {
    url,
    method,
    params,
    data,
    timeout,
    headers,
    responseType
  };

  const contentType = (headers as any)['Content-Type'];
  if (typeof contentType !== 'undefined') {
    if (contentType.indexOf('multipart') !== -1) { // 类型 `multipart/form-data;`
      defaultConfig.data = data;
    } else if (contentType.indexOf('json') !== -1) {  // 类型 `application/json`
      // 服务器收到的raw body(原始数据) "{name:"jhon",sex:"man"}"（普通字符串）
      defaultConfig.data = JSON.stringify(data);
    } else { // 类型 `application/x-www-form-urlencoded`       
      // 服务器收到的raw body(原始数据) name=homeway&key=nokey
      defaultConfig.data = qs.stringify(data);
    }
  }

  return axios(defaultConfig as AxiosRequestConfig);
}



/**
 * 5. 封装常用的get post put delete 和create方法
 */
export default {
  get (options: AxiosRequestConfig) {
    return request({ ...options, method: 'get' })
  },
  post (options: AxiosRequestConfig) {
    return request({ ...options, method: 'post' })
  },
  put (options: AxiosRequestConfig) {
    return request({ ...options, method: 'put' })
  },
  delete (options: AxiosRequestConfig) {
    return request({ ...options, method: 'delete' })
  },
  create (options: AxiosRequestConfig) {
    return request(options)
  }
}

/**
 * 6.导出参数限制
 * import http, { RequestType } from './http'
 * 
 */
export type RequestType = Pick<AxiosRequestConfig, 'url' | 'method' | 'headers' | 'data' | 'params' | 'responseType' | 'timeout'> // 取部分元素类型
